using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Drawing.Design;

namespace Acms.AcmsControls
{
    [ToolboxData("<{0}:UserRegisterControl runat=server></{0}:UserRegisterControl>")]
    [ParseChildren(true)]
    [PersistChildren(false)]
    public class UserRegisterControl : CompositeControl
    {
        protected override void OnLoad(EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                returnUrl = Page.Request.QueryString["ReturnUrl"];
            }
            base.OnLoad(e);
        }

        private string returnUrl;

        private string errorMessageClass = "errorMessage";
        public string ErrorMessageClass
        {
            get { return errorMessageClass; }
            set { errorMessageClass = value; }
        }

        private string usernameValidationExpression = @"^[a-zA-Z0-9]{1}[a-zA-Z0-9\S]{3,12}[a-zA-Z0-9]{1}$";
        public string UsernameValidationExpression
        {
            get { return usernameValidationExpression; }
            set 
            { 
                usernameValidationExpression = value; 
                if (valUsernameFormat != null)
                    valUsernameFormat.ValidationExpression = value; 
            }
        }

        private string userNameFormatErrorMessage = "Username must be between 5 and 14 characters, must start and end with a alphanumeric character and cannot contain spaces.";
        public string UserNameFormatErrorMessage
        {
            get { return userNameFormatErrorMessage; }
            set { userNameFormatErrorMessage = value; }
        }

        private string passwordValidationExpression = @"^[a-zA-Z][\S]{4,13}$";
        public string PasswordValidationExpression
        {
            get { return passwordValidationExpression; }
            set
            {
                passwordValidationExpression = value;
                if (valPasswordFormat != null)
                    valPasswordFormat.ValidationExpression = value;
            }
        }

        private string passwordFormatErrorMessage = "Password must start with a letter and be between 5 and 14 characters. No spaces are allowed.";
        public string PasswordFormatErrorMessage
        {
            get { return passwordFormatErrorMessage; }
            set { passwordFormatErrorMessage = value; }
        }
	

        private string emailValidationExpression = @"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z+])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$";
        public string EmailValidationExpression
        {
            get { return emailValidationExpression; }
            set
            {
                emailValidationExpression = value;
                if (valEmailFormat != null)
                    valEmailFormat.ValidationExpression = value;
            }
        }

        private string requiredErrorMessage = "*Required";
        public string RequiredErrorMessage
        {
            get { return requiredErrorMessage; }
            set { requiredErrorMessage = value; }
        }
	

        protected override object SaveViewState()
        {
            ViewState["ReturnUrl"] = returnUrl;
            ViewState["UsernameValidationExpression"] = UsernameValidationExpression;
            ViewState["UserNameFormatErrorMessage"] = UserNameFormatErrorMessage;
            ViewState["PasswordValidationExpression"] = PasswordValidationExpression;
            ViewState["PasswordFormatErrorMessage"] = PasswordFormatErrorMessage;
            ViewState["EmailValidationExpression"] = EmailValidationExpression;
            ViewState["UserCreated"] = userCreated;
            ViewState["RequiredErrorMessage"] = RequiredErrorMessage;
            return base.SaveViewState();
        }

        protected override void LoadViewState(object savedState)
        {
            base.LoadViewState(savedState);
            returnUrl =ViewState["ReturnUrl"] as string;
            UsernameValidationExpression = ViewState["UsernameValidationExpression"] as string;
            UserNameFormatErrorMessage = ViewState["UserNameFormatErrorMessage"] as string;
            PasswordValidationExpression = ViewState["PasswordValidationExpression"] as string;
            PasswordFormatErrorMessage = ViewState["PasswordFormatErrorMessage"] as string;
            EmailValidationExpression = ViewState["EmailValidationExpression"] as string;
            if (ViewState["UserCreated"] is bool)
                userCreated = (bool)ViewState["UserCreated"];
            RequiredErrorMessage = ViewState["RequiredErrorMessage"] as string;
        }

        TextBox username;
        RequiredFieldValidator valUsernameReq;
        RegularExpressionValidator valUsernameFormat;

        TextBox password;
        RequiredFieldValidator valPasswordReq;
        RegularExpressionValidator valPasswordFormat;

        TextBox confirmPassword;
        RequiredFieldValidator valConfirmPasswordReq;
        CompareValidator valConfirmPasswordMatch;

        TextBox email;
        RequiredFieldValidator valEmailReq;
        RegularExpressionValidator valEmailFormat;

        TextBox firstName;
        RequiredFieldValidator valFirstNameReq;

        TextBox lastName;
        RequiredFieldValidator valLastNameReq;

        TextBox dateOfBirth;
        RequiredFieldValidator valDOBReq;
        RangeValidator valDOBRange;

        RadioButton genderMale;
        RadioButton genderFemale;
        Acms.WebControls.CheckedRequiredValidator valGenderReq;

        TextBox address1;
        RequiredFieldValidator valAddress1Req;

        TextBox address2;

        TextBox town;
        RequiredFieldValidator valTownReq;

        TextBox postCode;
        RequiredFieldValidator valPostCodeReq;

        TextBox mobile;
        RequiredFieldValidator valMobileReq;

        Button btn;

        private ITemplate customFields;
        [System.Web.UI.TemplateInstance(TemplateInstance.Single)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Browsable(false)]
        public ITemplate CustomFields
        {
            get { return customFields; }
            set { customFields = value; }
        }

        private Control customFieldsContainer;

        protected override void CreateChildControls()
        {
            this.Controls.Clear();

            username = InitTextBox("username");

            valUsernameReq = new RequiredFieldValidator();
            InitValidiator(valUsernameReq, username, RequiredErrorMessage);

            valUsernameFormat = new RegularExpressionValidator();
            InitValidiator(valUsernameFormat, username, UserNameFormatErrorMessage);
            valUsernameFormat.ValidationExpression = UsernameValidationExpression;
            
            password = InitTextBox("password");
            password.TextMode = TextBoxMode.Password;

            valPasswordReq = new RequiredFieldValidator();
            InitValidiator(valPasswordReq, password, RequiredErrorMessage);

            valPasswordFormat = new RegularExpressionValidator();
            InitValidiator(valPasswordFormat, password, PasswordFormatErrorMessage);
            valPasswordFormat.ValidationExpression = PasswordValidationExpression;
            this.Controls.Add(valPasswordFormat);

            confirmPassword = InitTextBox("confirmPassword");
            confirmPassword.TextMode = TextBoxMode.Password;

            valConfirmPasswordReq = new RequiredFieldValidator();
            InitValidiator(valConfirmPasswordReq, confirmPassword, RequiredErrorMessage);

            valConfirmPasswordMatch = new CompareValidator();
            InitValidiator(valConfirmPasswordMatch, confirmPassword, "Passwords do not match.");
            valConfirmPasswordMatch.ControlToCompare = password.ID;
            valConfirmPasswordMatch.Operator = ValidationCompareOperator.Equal;

            email = InitTextBox("email");

            valEmailReq = new RequiredFieldValidator();
            InitValidiator(valEmailReq, email, RequiredErrorMessage);

            valEmailFormat = new RegularExpressionValidator();
            InitValidiator(valEmailFormat, email, "Invalid Email Address.");
            valEmailFormat.ValidationExpression = EmailValidationExpression;
            this.Controls.Add(valEmailFormat);

            firstName = InitTextBox("firstName");

            valFirstNameReq = new RequiredFieldValidator();
            InitValidiator(valFirstNameReq, firstName, RequiredErrorMessage);

            lastName = InitTextBox("lastName");

            valLastNameReq = new RequiredFieldValidator();
            InitValidiator(valLastNameReq, lastName, RequiredErrorMessage);

            dateOfBirth = InitTextBox("dateOfBirth");
            
            valDOBReq = new RequiredFieldValidator();
            InitValidiator(valDOBReq, dateOfBirth, RequiredErrorMessage);

            valDOBRange = new RangeValidator();
            InitValidiator(valDOBRange, dateOfBirth, "Invalid Date of Birth");
            valDOBRange.MinimumValue = DateTime.Today.AddYears(-100).ToString("yyyy/MM/dd");
            valDOBRange.MaximumValue = DateTime.Today.AddYears(-1).ToString("yyyy/MM/dd");
            valDOBRange.CultureInvariantValues = true;
            valDOBRange.Type = ValidationDataType.Date;

            genderMale = new RadioButton();
            InitControl(genderMale, "male");
            genderMale.GroupName = string.Concat(this.ClientID, "gender");

            genderFemale = new RadioButton();
            InitControl(genderFemale, "female");
            genderFemale.GroupName = string.Concat(this.ClientID, "gender");

            valGenderReq = new Acms.WebControls.CheckedRequiredValidator();
            InitValidiator(valGenderReq, genderMale, RequiredErrorMessage);
            valGenderReq.ControlsToValidate = new string[] { genderMale.ID, genderFemale.ID };

            address1 = InitTextBox("address1");
            valAddress1Req = new RequiredFieldValidator();
            InitValidiator(valAddress1Req, address1, RequiredErrorMessage);

            address2 = InitTextBox("address2");

            town = InitTextBox("town");
            valTownReq = new RequiredFieldValidator();
            InitValidiator(valTownReq, town, RequiredErrorMessage);

            postCode = InitTextBox("postCode");
            valPostCodeReq = new RequiredFieldValidator();
            InitValidiator(valPostCodeReq, postCode, RequiredErrorMessage);

            mobile = InitTextBox("mobile");

            valMobileReq = new RequiredFieldValidator();
            InitValidiator(valMobileReq, mobile, RequiredErrorMessage);

            btn = new Button();
            btn.ID = "btn";
            btn.Text = "Register";
            btn.ValidationGroup = this.ClientID;
            btn.Click += new EventHandler(btn_Click);
            this.Controls.Add(btn);

            if (CustomFields != null)
            {
                customFieldsContainer = new PlaceHolder();
                this.Controls.Add(customFieldsContainer);
                CustomFields.InstantiateIn(customFieldsContainer);
                foreach (Control c in customFieldsContainer.Controls)
                {
                    if (c is BaseValidator && string.IsNullOrEmpty(((BaseValidator)c).ValidationGroup))
                    {
                        ((BaseValidator)c).ValidationGroup = this.ClientID;
                    }
                }
            }

            base.CreateChildControls();
        }

        private TextBox InitTextBox(string id)
        {
            TextBox tmp = new TextBox();
            InitControl(tmp, id);
            return tmp;
        }

        private void InitControl(Control ctrl, string id)
        {
            ctrl.ID = id;
            this.Controls.Add(ctrl);
        }

        private void InitValidiator(BaseValidator val, Control controlToValidate, string errorMessage)
        {
            val.ErrorMessage = errorMessage;
            val.ControlToValidate = controlToValidate.ID;
            val.ValidationGroup = this.ClientID;
            val.Display = ValidatorDisplay.Dynamic;
            this.Controls.Add(val);
        }

        void btn_Click(object sender, EventArgs e)
        {
            if (Page.IsValid)
            {
                int tmpSiteId = 0;
                if (Page is TemplatePage)
                {
                    tmpSiteId = ((TemplatePage)Page).SiteId;
                }
                else 
                {
                    Acms.Bll.Site site = Acms.Bll.Site.GetFromHttpRequest(Page.Request);
                    if(site != null)
                        tmpSiteId = site.Id;
                    else
                        throw new Acms.Exceptions.PostNotFoundException(string.Format("Site at address '{0}' not found.", Acms.Bll.Site.GetSiteRootFromHttpRequest(Page.Request)));
                }

                Acms.Bll.UserSecurity user = Acms.Bll.UserSecurity.CreateInstance(tmpSiteId);
                user.UserName = username.Text;
                user.SetPassword(null, password.Text);
                user.EmailAddress = email.Text;
                user.FirstName = firstName.Text;
                user.LastName = lastName.Text;
                user.DateOfBirth = DateTime.Parse(dateOfBirth.Text);
                
               
                if(genderMale.Checked)
                    user.Gender = Acms.Bll.User.Genders.Male;
                else
                    user.Gender = Acms.Bll.User.Genders.Female;

                user.Address1 = address1.Text;
                user.Address2 = address2.Text;
                user.Town = town.Text;
                user.PostCode = postCode.Text;
                user.MobileTel = mobile.Text;


                OnUserRegistering(new UserRegisterEventArgs(user));

                try
                {
                    user.SetLogedOn();
                    user.Update();
                    userCreated = true;
                }
                catch (Acms.Exceptions.DuplicateUsernameException)
                {
                    duplicateUsername = true;
                }
                catch (Acms.Exceptions.DuplicateEmailException)
                {
                    duplicateEmail = true;
                }

                if (userCreated)
                {
                    //set cookie to log in
                    Guid key = user.GetLoginKey(false);
                    System.Web.Security.FormsAuthentication.SetAuthCookie(key.ToString(), false);
                    OnUserRegistered(new UserRegisterEventArgs(user));
                }
            }
        }

        private void OnUserRegistered(UserRegisterEventArgs e)
        {
            if (UserRegistered != null)
            {
                UserRegistered(this, e);
            }
        }

        public event EventHandler<UserRegisterEventArgs> UserRegistered;

        private void OnUserRegistering(UserRegisterEventArgs e)
        {
            if (UserRegistering != null)
            {
                UserRegistering(this, e);
            }
        }

        public event EventHandler<UserRegisterEventArgs> UserRegistering;

        bool duplicateEmail;
        bool duplicateUsername;
        bool userCreated;

        public override void RenderControl(HtmlTextWriter writer)
        {
            //wrapper div
            writer.AddAttribute("OnKeyPress", string.Format("javascript:return WebForm_FireDefaultButton(event, '{0}')", btn.ClientID));
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "register");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            if (Page != null && Page.Request != null && Page.Request.IsAuthenticated)
            {
                writer.Write("You are logged in as '{0}'.", Page.User.Identity.Name); 
            }
            else if (userCreated)
            {
                writer.Write("Congratulations! You are now registered");

                string tmp = returnUrl;
                if (string.IsNullOrEmpty(tmp))
                {
                    tmp = "~/";
                }

                writer.AddAttribute(HtmlTextWriterAttribute.Href, Page.ResolveUrl(tmp));
                writer.RenderBeginTag(HtmlTextWriterTag.A);
                writer.Write("Continue");
                writer.RenderEndTag();
            }
            else
            {
                if (duplicateEmail || duplicateUsername)
                {
                    writer.AddAttribute(HtmlTextWriterAttribute.Class, ErrorMessageClass);
                    writer.RenderBeginTag(HtmlTextWriterTag.Div);
                    if (duplicateEmail)
                    {
                        writer.RenderBeginTag(HtmlTextWriterTag.Span);
                        writer.Write("This email address belongs to another user.");
                        writer.RenderEndTag();
                    }
                    if (duplicateUsername)
                    {
                        writer.RenderBeginTag(HtmlTextWriterTag.Span);
                        writer.Write("This username belongs to another user.");
                        writer.RenderEndTag();
                    }
                    writer.RenderEndTag();
                }
                //render ul
                writer.RenderBeginTag(HtmlTextWriterTag.Ul);

                RenderUsername(writer);
                RenderPassword(writer);
                RenderConfrimPassword(writer);
                RenderEmail(writer);
                RenderFirstName(writer);
                RenderLastName(writer);
                RenderDateOfBirth(writer);
                RenderGender(writer);
                RenderAddress(writer);
                RenderMobile(writer);

                if (CustomFields != null)
                {
                    customFieldsContainer.RenderControl(writer);
                }
                //button
                RenderButton(writer);

                writer.RenderEndTag();

            }
            writer.RenderEndTag();
        }


        private void RenderItemWrapperStart(HtmlTextWriter writer, string cssClass)
        {
            string tmp = string.Concat("register ", cssClass);
            writer.AddAttribute(HtmlTextWriterAttribute.Class, tmp.Trim());
            writer.RenderBeginTag(HtmlTextWriterTag.Li);

        }

        private void RenderItemWrapperEnd(HtmlTextWriter writer)
        {
            writer.RenderEndTag();
        }

        private void RenderUsername(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "username");
            RenderLabel("Username:", username, writer);
            username.RenderControl(writer);
            valUsernameReq.RenderControl(writer);
            valUsernameFormat.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderPassword(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "password");
            RenderLabel("Password:", password, writer);
            password.RenderControl(writer);
            valPasswordReq.RenderControl(writer);
            valPasswordFormat.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderConfrimPassword(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "confirmPassword");
            RenderLabel("Confirm Password:", confirmPassword, writer);
            confirmPassword.RenderControl(writer);
            valConfirmPasswordReq.RenderControl(writer);
            valConfirmPasswordMatch.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderEmail(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "email");
            RenderLabel("Email:", email, writer);
            email.RenderControl(writer);
            valEmailReq.RenderControl(writer);
            valEmailFormat.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderFirstName(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "firstname");
            RenderLabel("First Name:", firstName, writer);
            firstName.RenderControl(writer);
            valFirstNameReq.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }


        private void RenderLastName(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "lastName");
            RenderLabel("Last Name:", lastName, writer);
            lastName.RenderControl(writer);
            valLastNameReq.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderDateOfBirth(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "dob");
            RenderLabel("Date of Birth:", dateOfBirth, writer);
            dateOfBirth.RenderControl(writer);
            System.Globalization.CultureInfo info = System.Globalization.CultureInfo.CurrentCulture;
            //if(Page != null && Page.Request != null && Page.Request.UserLanguages != null && Page.Request.UserLanguages.Length > 0)
            //{
                //info = new System.Globalization.CultureInfo(Page.Request.UserLanguages[0]);
            //}
            writer.Write("({0})", info.DateTimeFormat.ShortDatePattern.Replace("M", "m"));
            valDOBReq.RenderControl(writer);
            valDOBRange.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderGender(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "gender");
            //writer.RenderBeginTag(HtmlTextWriterTag.Span);
            //writer.Write("Gender:");
            //writer.RenderEndTag();
            RenderLabel("Male", genderMale, writer);
            genderMale.RenderControl(writer);
            RenderLabel("Female", genderFemale, writer);
            genderFemale.RenderControl(writer);
            valGenderReq.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderAddress(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "address1");
            RenderLabel("Address (Line 1):", lastName, writer);
            address1.RenderControl(writer);
            valAddress1Req.RenderControl(writer);
            RenderItemWrapperEnd(writer);

            RenderItemWrapperStart(writer, "address2");
            RenderLabel("Address (Line 2):", lastName, writer);
            address2.RenderControl(writer); 
            RenderItemWrapperEnd(writer);

            RenderItemWrapperStart(writer, "town");
            RenderLabel("Town / City:", lastName, writer);
            town.RenderControl(writer);
            valTownReq.RenderControl(writer);
            RenderItemWrapperEnd(writer);

            RenderItemWrapperStart(writer, "postcode");
            RenderLabel("PostCode:", lastName, writer);
            postCode.RenderControl(writer);
            valPostCodeReq.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderMobile(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "mobile");
            RenderLabel("Mobile:", lastName, writer);
            mobile.RenderControl(writer);
            valMobileReq.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }

        private void RenderLabel(string text, Control associatedControl, HtmlTextWriter writer)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.For, associatedControl.ClientID);
            writer.RenderBeginTag(HtmlTextWriterTag.Label);
            writer.Write(text);
            writer.RenderEndTag();
        }

        private void RenderButton(HtmlTextWriter writer)
        {
            RenderItemWrapperStart(writer, "submit");
            btn.RenderControl(writer);
            RenderItemWrapperEnd(writer);
        }
    }

    public class UserRegisterEventArgs : EventArgs
    {
        public UserRegisterEventArgs(Acms.Bll.UserSecurity user)
        {
            this.user = user;
        }

        private Acms.Bll.UserSecurity user;
        public Acms.Bll.UserSecurity User
        {
            get { return user; }
        }
	
    }
}
